﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using NExplorer.Controls;

namespace NExplorer
{
	internal class ExplorerService : IExplorerService
	{
		public event EventHandler				SelectedItemsChanging;
		public event EventHandler				SelectedItemsChanged;
		public event EventHandler				CurrentDirectoryChanging;
		public event EventHandler				CurrentDirectoryChanged;
		public event EventHandler				NavigateBackSelected;
		public event EventHandler				NavigateForwardSelected;
		public event ColumnItemUpdatedHandler	ColumnItemUpdated;
        public event EventHandler               RefreshRequested;

	    private IList<FileSystemInfo>			_selectedItems = new FileSystemInfo[0];
		private DirectoryInfo					_currentDirectory;
		private SelectionInfo					_selectionInfo;
		private readonly HistoryList			_historyList = new HistoryList();

		public IList<FileSystemInfo> SelectedItems
		{
			get { return _selectedItems; }
			set
			{
				if (SelectedItemsChanging != null)
				{
					SelectedItemsChanging(this, EventArgs.Empty);	
				}

				_selectedItems = value ?? new FileSystemInfo[0];
				_selectionInfo = null;

				if (SelectedItemsChanged != null)
				{
					SelectedItemsChanged(this, EventArgs.Empty);
				}
			}
		}

		public HistoryList HistoryList
		{
			get { return _historyList; }	
		}

		public DirectoryInfo CurrentDirectory
		{
			get { return _currentDirectory; }
		}

		private void SetCurrentDirectory(DirectoryInfo directory, bool addToHistory)
		{
			if (!directory.IsEqualTo(_currentDirectory))
			{
				if (CurrentDirectoryChanging != null)
				{
					CurrentDirectoryChanging(this, EventArgs.Empty);
				}

				_currentDirectory = directory;

                SelectedItems = new FileSystemInfo[] { directory };

				if (addToHistory)
				{
					_historyList.Add(directory);
				}

				if (CurrentDirectoryChanged != null)
				{
					CurrentDirectoryChanged(this, EventArgs.Empty);
				}
			}
		}

		public SelectionInfo SelectionInfo
		{
			get
			{
				if (_selectionInfo == null)
				{
					List<LowerCaseString> extensions = new List<LowerCaseString>();
					bool includesDirectory = false;

					foreach (FileSystemInfo info in SelectedItems)
					{
						FileInfo fileInfo = info as FileInfo;

						if (fileInfo != null)
						{
							LowerCaseString extension;
						
							if (fileInfo.Extension != "")
							{
								extension = new LowerCaseString(fileInfo.Extension.Substring(1));
							}
							else
							{
								extension = LowerCaseString.Empty;
							}

							if (!extensions.Contains(extension))
							{
								extensions.Add(extension);
							}
						}
						else // Only other option is DirectoryInfo
						{
							includesDirectory = true;
						}
					}

					_selectionInfo = new SelectionInfo(extensions, includesDirectory);
				}

				return _selectionInfo;
			}
		}

		public void UpdateColumnItem(Guid directoryId, int columnId, FileSystemInfo fileSystemInfo, ColumnItemData data)
		{
			if (ColumnItemUpdated != null)
			{
				ColumnItemUpdated(directoryId, columnId, fileSystemInfo, data);
			}
		}

		public void NavigateTo(FileSystemInfo fileSystemInfo)
		{
			NavigateTo(fileSystemInfo, true);
		}

		private void NavigateTo(FileSystemInfo fileSystemInfo, bool addToHistory)
		{
			if (fileSystemInfo == null)
			{
			    throw new ArgumentNullException("filesSystemInfo");
			}
			else if (fileSystemInfo.Exists)
			{
				if (fileSystemInfo is DirectoryInfo)
				{
					SetCurrentDirectory((DirectoryInfo)fileSystemInfo, addToHistory);
				}
				else
				{
					ProcessStartInfo startInfo = new ProcessStartInfo(fileSystemInfo.FullName);
					startInfo.ErrorDialog = true;

					Process.Start(startInfo);
				}
			}
			else
			{
				MessageBox.Show(fileSystemInfo.FullName + " does not exist.", "Invalid Name", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		public void NavigateBack()
		{
			NavigateTo(_historyList.MoveBack(), false);
		}

		public void NavigateForward()
		{
			NavigateTo(_historyList.MoveForward(), false);
		}

		public bool CanNavigateBack
		{
			get { return _historyList.CanMoveBack; }
		}

		public bool CanNavigateForward
		{
			get { return _historyList.CanMoveForward; }
		}

		public void Refresh()
		{
			if (RefreshRequested != null)
			{
				RefreshRequested(this, EventArgs.Empty);
			}
		}

		public ContextMenuStrip CreateContextMenu()
		{
			MenuBuilderManager manager = (MenuBuilderManager)ServiceManager.MenuBuilderManager;
			Dictionary<string,List<ICommand>> allCommands = new Dictionary<string, List<ICommand>>();

			foreach (IMenuBuilder menuBuilder in manager.MenuBuilders)
			{
				IList<MenuCommandInfo> menuCommandInfos = menuBuilder.GetMenuCommandInfos();

				foreach (MenuCommandInfo commandInfo in menuCommandInfos)
				{
					List<ICommand> commandsInCategory;

					if (!allCommands.TryGetValue(commandInfo.Category, out commandsInCategory))
					{
						commandsInCategory = new List<ICommand>();
						allCommands.Add(commandInfo.Category, commandsInCategory);
					}

					commandsInCategory.Add(commandInfo.Command);
				}
			}

			ExplorerContextMenu menu = new ExplorerContextMenu();
			menu.Renderer = Utilities.GetMenuStripRenderer();

			// Assemble the menu in a particular order: Verbs, custom-named, General, Clipboard and finally Properties
			AddMenuItems(menu, "Verbs", allCommands);

			foreach (string category in allCommands.Keys)
			{
				switch (category)
				{
					case "Verbs":
					case "General":
					case "Clipboard":
					case "File":
					case "Properties":
						break;

					default:
						AddMenuItems(menu, allCommands[category]);
						break;
				}
			}

			// Add 'Send To' menu and subitems
			ToolStripMenuItem sendToMenuItem = (ToolStripMenuItem)menu.Items.Add("Send To");

			foreach (string file in Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.SendTo)))
			{
				sendToMenuItem.DropDownItems.Add(new SendToMenuItem(new FileInfo(file)));
			} 
			
			menu.AddSeparator();

			AddMenuItems(menu, "General", allCommands);
			AddMenuItems(menu, "Clipboard", allCommands);
			AddMenuItems(menu, "File", allCommands);
			AddMenuItems(menu, "Properties", allCommands);

			// Remove final seperator.
			menu.Items.RemoveAt(menu.Items.Count - 1);
			
			// Bold first verb
			menu.Items[0].Font = new Font(menu.Font, FontStyle.Bold);

			//// Add items from IMenuBuilders that have been added from AddIns.
			//AddCommandMenuItems(menu, extensions);

			//menu.AddSeparator();

			//// Add appropriate Cut/Copy/Paste for selected items
			//menu.AddCommands(new ClipboardMenuBuilder().GetFileSystemCommands());

			//// Add shortcut, rename and delete.
			//menu.AddSeparator();
			//menu.Items.Add("Create Shortcut");
			//menu.Items.Add("Rename");
			//menu.AddCommand(new DeleteCommand());

			//// Add Properties.
			//menu.AddSeparator();
			//menu.AddCommand(new PropertiesCommand());

			return menu;
		}

		private void AddMenuItems(ExplorerContextMenu menu, string category, Dictionary<string,List<ICommand>> allCommands)
		{
			List<ICommand> commands;

			if (allCommands.TryGetValue(category, out commands))
			{
				AddMenuItems(menu, commands);
			}
		}

		private void AddMenuItems(ExplorerContextMenu menu, List<ICommand> commands)
		{
			menu.AddCommands(commands);
			menu.AddSeparator();
		}
	}
}
